
package uk.ac.cam.cl.oscar.edgefinder;

import uk.ac.cam.cl.oscar.*;
import uk.ac.cam.cl.oscar.sift.*;
import java.util.*;
import java.awt.Point;
import java.awt.image.BufferedImage;
import javax.media.jai.*;

public class EdgeFinder {

	private static float[] rotateMatrix (float[] mtx, int mw, int mh) {
		float[] rmtx = new float[mw * mh];
		
		for (int i = 0; i < mh; i++) {
			for (int j = 0; j < mw; j++) {
				rmtx[i * mw + j] = mtx[j * mw + (mw - i - 1)];
			}
		}
		
		return rmtx;
	}

	public static BufferedImage findEdges (BufferedImage input)
	{
		/* Apply Difference of Gausians, Sobel, or anything else handy... */
		
		// Set up matrix
		int mw = 5; int mh = 5;
		float[] fMtx = new float[mw*mh];
		
		for (int i = 0; i < mh; i++) {
			for (int j = 0; j < mw; j++) fMtx[i*mw + j] = 0.0f;
			fMtx[i*mw] = -1.0f;
			fMtx[i*mw + mw-1] = 1.0f;
		}
		
		float[] mtx = fMtx;
		
		// Set up output image
		BufferedImage output = new BufferedImage (input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_RGB);
		
		for (int i = 0; i < 4; i++) {
			BufferedImage edges = ImageOperations.convolve2D(input, mtx, mw, mh);
			for (int x = 0; x < edges.getWidth(); x++) {
				for (int y = 0; y < edges.getHeight(); y++) {
					int a = edges.getRGB(x, y);
					int b = output.getRGB(x, y);
					int c = (a&0xFF+(a>>>8)&0xFF+(a>>>16)&0xFF) + (b&0xFF+(b>>>8)&0xFF+(b>>>16)&0xFF);
					
					output.setRGB (x, y, c & 0xFF);
				}
			}
			mtx = rotateMatrix (mtx, mw, mh);
		}
		
		return output;
	}

	private static void drawRhoLine (BufferedImage image, RhoLine line)
	{
		double xstep = 0.03;
		double toScreen = NormalisedPoint.toScreenSF (image.getWidth(), image.getHeight());
		
		assert (false); // This is a broken function.
		
		for (double x = 0; x < image.getWidth(); x+=xstep) 
		{	
			double xp = -(double)image.getWidth()/2 + x;
			double yp = (-Math.cos(line.theta) / Math.sin(line.theta))*xp + 
				line.rho * toScreen / Math.sin(line.theta);
			int y = (int) Math.floor (yp) + image.getHeight()/2;
		
			if (y >= 0 && y < image.getHeight() ) 
			{
				image.setRGB ((int)x, (int)y, 0xFF00FF);	
			}
		}
	}

	public static BufferedImage extractRectangle (BufferedImage image)
	{
		BufferedImage edgy = EdgeFinder.findEdges (image);
                
                // display the edgy image in the demo app
                if(Demonstration.DemoAppInst != null){
                    Demonstration.DemoAppInst.displayNewEdgyImage("", edgy);
                }
                
		RayPackage rays = ImageRaytracer.raytrace (edgy);
		
		for (int i = 0; i < rays.points.length; i++)
		{
			ArrayList<Point> list = (ArrayList<Point>) rays.points[i];
			if (list.size() == 0) {
				System.out.println ("No points in " + i + "th list");
				return image;
			}
			
			/** DEBUG RayTrace point printer */
			/*for (Point p : list) 
			{
				image.setRGB (p.x, p.y, 0x00FFFF);
			}*/
		}
		
		LineFinder lf = new LineFinder (0.003f, 0.006f);
		
		RhoLine[] lines = new RhoLine[4];
		NormalisedPoint[] corners = new NormalisedPoint[4];
		
		Iterator j; Point p = ((List<Point>)rays.points[0]).get(0); 
		
		for (int i = 0; i < 4; i++) {
			for (j = ((List<Point>)rays.points[i]).iterator(); j.hasNext(); p = (Point)j.next()) {
				NormalisedPoint np = new NormalisedPoint();
				np.initFromScreen (p.x, p.y, edgy.getWidth(), edgy.getHeight());
				lf.addPoint (new NormalisedPoint (np));
			}
			lines[i] = lf.findLine();
			//drawRhoLine (image, lines[i]);
			lf.clearPoints();
		}
		
		boolean goodCornerSet = true;
		CornerSorter csort = new CornerSorter();
		
		for (int i = 0; i < 4 && goodCornerSet; i++) {
			corners[i] = lines[i].intersect (lines[ (i+1)%4 ]);
			csort.add (corners[i]);
			
			int x = corners[i].screenX(image.getWidth(), image.getHeight());
			int y = corners[i].screenY(image.getWidth(), image.getHeight());
			
			if (x < 0 || x > image.getWidth() || y < 0 || y > image.getHeight())
			{
				goodCornerSet = false;
			}
			/*** DEBUG CORNERS */
			/*else {
				
				image.setRGB (x,y, 0xFFFF00);
				image.setRGB (x+1,y, 0xFFFF00);
				image.setRGB (x,y+1, 0xFFFF00);
			}*/
		}
		
		if (goodCornerSet && csort.pointCount() >= 4) 
		{
			try {
				NormalisedPoint tl = csort.getTopLeft();
				NormalisedPoint tr = csort.getTopRight();
				NormalisedPoint bl = csort.getBottomLeft();
				NormalisedPoint br = csort.getBottomRight();
                                
                                
				
				System.out.println ("tl x: " + tl.x() + " y: " + tl.y());
				System.out.println ("tr x: " + tr.x() + " y: " + tr.y());
				System.out.println ("bl x: " + bl.x() + " y: " + bl.y());
				System.out.println ("br x: " + br.x() + " y: " + br.y());
			
				double sf = NormalisedPoint.toScreenSF (image.getWidth(), image.getHeight());
				double width = sf*NormalisedPoint.normalDistance (tl, tr);
				double height = sf*NormalisedPoint.normalDistance (tl, bl);
			
				PerspectiveTransform pt = PerspectiveTransform.getQuadToQuad(		
					0,0,
					0, height,
					width, height,
					width, 0,
					
					tl.screenX(image.getWidth(), image.getHeight()), 
					tl.screenY(image.getWidth(), image.getHeight()),
					
					bl.screenX(image.getWidth(), image.getHeight()), 
					bl.screenY(image.getWidth(), image.getHeight()),
					
					br.screenX(image.getWidth(), image.getHeight()), 
					br.screenY(image.getWidth(), image.getHeight()),
					
					tr.screenX(image.getWidth(), image.getHeight()), 
					tr.screenY(image.getWidth(), image.getHeight())
				);
				
				BufferedImage warped = ImageOperations.perspectiveTransform (image, pt);
				BufferedImage cropped = ImageOperations.shrinkCanvas (warped, (int)width, (int)height, false);
                                
                                if(Demonstration.DemoAppInst != null){
                                    Demonstration.DemoAppInst.displayNewFramedImage(
                                        tl.screenX(image.getWidth(), image.getHeight()), 
					tl.screenY(image.getWidth(), image.getHeight()),
					
					tr.screenX(image.getWidth(), image.getHeight()), 
					tr.screenY(image.getWidth(), image.getHeight()),
					
					bl.screenX(image.getWidth(), image.getHeight()), 
					bl.screenY(image.getWidth(), image.getHeight()),
					
					br.screenX(image.getWidth(), image.getHeight()), 
					br.screenY(image.getWidth(), image.getHeight())
                                        );
                                }
		
			   return cropped;
			}
			catch (Exception e) {
				System.out.println ("Not enough corners detected to extract a rectangle");
                                if(Demonstration.DemoAppInst != null){
                                    Demonstration.DemoAppInst.skipFramedImage();
                                }
				return image;
			}
       }
       else
       return image;
	
	}

}